package exebuilder.controller;

import exebuilder.core.Controllers;
import exebuilder.core.Windows;
import exebuilder.data.JdkDownloadConfig;
import exebuilder.ui.ExeBuilderWindow;
import exebuilder.ui.MessageBox;
import javafx.application.Platform;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import org.apache.http.HeaderElement;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.ResourceBundle;

public class DownloadJDKProcessController implements Initializable {

 private static final Logger logger = LogManager.getLogger();
 public ProgressBar downloadJdkProgressBar;
 public Label downloadProcessLabel;
 public Button cancelButton;
 public Label speedLabel;
 double countLen = 0;
 double ratio = 0;
 public static int TIME_OUT = 30 * 1000;
 Thread downloadJdkThread;
 ExeBuilderWindow downloadJdkProcessWindow = Windows.get("downloadJdkProcess");
 JdkDownloadConfig jdkDownloadConfig = JdkDownloadConfig.getInstance();

 public void downloadJDK(String urlPath, String targetDirectory) throws IOException, NoSuchAlgorithmException {
  var df = new DecimalFormat("0.00");
  SSLContext sslContext = SSLContext.getDefault();
  SSLConnectionSocketFactory sslConnectionSocketFactory
    = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
  Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslConnectionSocketFactory)
    .register("http", PlainConnectionSocketFactory.INSTANCE)
    .build();
  var ccm = new BasicHttpClientConnectionManager(registry);
  var httpClient = HttpClientBuilder.create().
    setSSLSocketFactory(sslConnectionSocketFactory)
    .setConnectionManager(ccm).build();
  var httpGet = new HttpGet(urlPath);
  var response = httpClient.execute(httpGet);
  var entity = response.getEntity();
  var length = entity.getContentLength();
  logger.info("JDK文件大小：" + (length / 1024) + "KB");
  var fileName = getFileName(response, urlPath);
  var inputStream = entity.getContent();
  var buff = new byte[(int) length];
  var destFile = new File(targetDirectory, fileName);
  if (destFile.exists()) {
   destFile.delete();
  }
  var out = new FileOutputStream(destFile);
  int len;
  logger.info("开始下载...");
  var startTime = System.currentTimeMillis();
  while ((len = inputStream.read(buff)) != -1) {
   if (downloadJdkThread.isInterrupted()) {
    logger.info("下载中断");
    break;
   }
   var currentTime = System.currentTimeMillis();
   var timeDiff = (currentTime - startTime) / 1000;
   out.write(buff, 0, len);
   out.flush();
   countLen += len;
   var value = timeDiff == 0 ? 0 : (countLen / timeDiff) / 1024;
   ratio = countLen / length;
   Platform.runLater(() -> {
    downloadJdkProgressBar.setProgress(ratio);
    downloadProcessLabel.setText(df.format(ratio * 100) + "%");
    if (value > 1024) {
     speedLabel.setText(df.format(value / 1024) + "MB/s");
    } else {
     speedLabel.setText(df.format(value) + "KB/s");
    }
   });
  }
  out.close();
  inputStream.close();
 }

 public static String getFileName(HttpResponse response, String urlPath) {
  var contentHeader = response.getFirstHeader("Content-Disposition");
  String fileName = null;
  if (contentHeader != null) {
   HeaderElement[] values = contentHeader.getElements();
   if (values.length == 1) {
    NameValuePair param = values[0].getParameterByName("filename");
    if (param != null) {
     fileName = param.getValue();
     logger.info("JDK文件名：" + fileName);
    }
   }
  }
  if (null == fileName) {
   var arr = urlPath.split("/");
   fileName = arr[arr.length - 1];
   logger.info("url中获取JDK文件名：" + fileName);
  }
  logger.debug("JDK文件名：" + fileName);
  return fileName;
 }


 public void cancelDownload() {
  downloadJdkThread.interrupt();
  downloadJdkProcessWindow.close();
 }

 @Override
 public void initialize(URL url, ResourceBundle resourceBundle) {
  Controllers.add("downloadJdkProcess", this);
  downloadJdkProcessWindow.setOnCloseRequest(windowEvent -> cancelDownload());
  downloadJdkThread = new Thread(() -> {
   try {
    downloadJDK(jdkDownloadConfig.getUrl(), jdkDownloadConfig.getOutputPath());
    logger.info("JDK下载完毕");
    Platform.runLater(() -> speedLabel.setText("下载完毕"));
   } catch (IOException | NoSuchAlgorithmException e) {
    logger.error(e);
    Platform.runLater(() -> MessageBox.error(e.getMessage()));
   }
  });
  downloadJdkThread.start();
 }
}
